/* Include files. */
extern "C" { 
	#include "standard.h"
	#include "debug.h"
}
	
#include "network.h"
#include "netwebcgi.h"
#include "nettr069.h"
#include "netcli.h"

/* Local function declarations. */

/* Macro API definitions. */

/* Global variable declarations. */

/* Type definitions. */

struct connectionType {
	int port;
	e_net_object type;
	string name;
	int fd;
	
#ifdef U_LOOP	
	uloop_fd h;
#else
	ev_io h;
#endif

};

/* Local function definitions. */

static int sockoptReuseaddr(int sock)
{
	int ret;
	int on = 1;

	ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on));
	if (ret < 0) {
		ERROR("can't set sockopt SO_REUSEADDR to socket %d errno %s\n", sock,
			  strerror(errno));
		return -1;
	}
	
	return 0;
}

static int createTcpServer(u32 ip, int port)
{
	int	sock, ret;
	struct sockaddr_in addr;

	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0) {
		ERROR("create_tcp_server socket failed!\n");
		return -1;
	}

	memset(&addr, 0, sizeof(struct sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = htonl(ip);
	sockoptReuseaddr(sock);

	ret = bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
	if (ret < 0) {
		ERROR("create_tcp_server Can't bind to socket(ip=%u.%u.%u.%u, port=%d): %s\n",
			  NIPQUAD(ip), port, strerror(errno));
		close(sock);
		return -1;
	}

	ret = listen(sock, SOMAXCONN);
	if (ret < 0) {
		ERROR("create_tcp_server Can't listen to socket(port=%d): %s\n", port,
			  strerror(errno));
		close(sock);
		return -1;
	}
	
	return sock;
}

#ifndef U_LOOP
static void evReadMsg(struct ev_loop *loop, ev_io *h, int revents)
{
	netObject *netobj = (netObject *)h->data;
	int ret ;
	
	if (e_OK == (ret = netobj->recvMsg())) {
		if (netobj->_out_queue.size() > 0) {
			ev_io_stop(loop, h);
			ev_io_init(h, evWriteMsg, h->fd, EV_WRITE);
			ev_io_start(loop, h);	
		}	
	}
	else if (e_ERROR == ret) {
		goto error;
	}
	
	return;

error:
	delete netobj;
	return;
}

void evWriteMsg(struct ev_loop *loop, ev_io *h, int revents)
{
	netObject *netobj = (netObject *)h->data;

start:
	if (netobj->_out_queue.size() == 0) {
		goto finish;
	}

	if ( e_OK == netobj->sendMsg()) {
		goto start;
	}
	else {
		delete netobj;
		return;
	}

finish:
	ev_io_stop(loop, h);
	ev_io_init(h, evReadMsg, h->fd, EV_READ);
	ev_io_start(loop, h);

	return;
}
#endif

#ifdef U_LOOP
static void createNetObject(u32 ip, u16 port, 
				int fd, connectionType *conn)

#else
static void createNetObject(u32 ip, u16 port, 
				u32 fd, struct ev_loop *loop, 
				connectionType *conn)
#endif

{
	netObject *client = NULL;
	switch (conn->type) {
		case e_CLI:
			client = new netCli(ip, port, fd);
			break;
		case e_CGI:
			client = new netWebcgi(ip, port, fd);
			break;
		case e_TR069:
			client = new nettr069(ip, port, fd);
			break;

		/*TODO: add connectionType*/
		default:
			ERROR("e_net_object is error!\n");
			ASSERT(0);
			break;
	}
	if (client == NULL) {
		ERROR("create netobject faild, out of memory!\n");
		return;
	}

#ifdef U_LOOP
	if (!client->bind()) {
		delete client;
		return;
	}
#else
	client->_loop = loop;
	ev_io_init(&client->_h, evReadMsg, client->_fd, EV_READ);
	if (client->bind()) {		
		client->_h.data = client;
		ev_io_start(loop, &client->_h);
	}
	else {
		delete client;
		return;
	}
#endif

	client->accept();

	return;
}

#ifdef U_LOOP
static void acceptConnection(uloop_fd *h, unsigned int revents)
#else
static void acceptConnection(struct ev_loop *loop, 
						ev_io *h, int revents)
#endif
{
	int newfd;
	struct sockaddr_in addr;
	socklen_t addr_len;
	addr_len = sizeof(addr);

	while((newfd = accept(h->fd, (struct sockaddr *)&addr, &addr_len)) < 0){
		if (TCP_NORMAL_ERR(errno)) {
			continue;
		} else {
			ERROR("newfd = %d, accept error.[%s]\n", newfd,
				  strerror(errno));
			return;
		}
	}
	
	connectionType *conn = (connectionType *)h->data;
	
#ifdef U_LOOP
	createNetObject(ntohl(addr.sin_addr.s_addr),
		ntohs(addr.sin_port), newfd, conn);
#else
	createNetObject(ntohl(addr.sin_addr.s_addr),
		ntohs(addr.sin_port), newfd, loop, conn);
#endif
	
	return;
}

#ifdef U_LOOP
void createServiceServer()
#else
void createServiceServer(struct ev_loop *loop)
#endif
{
	static connectionType conns[] = {
		{9000, e_CGI,     "cgi"},
		{9001, e_TR069,   "tr069"},
		{9002, e_CLI, 	  "cli"},
		/*TODO.. other connection type*/
	};

	for(u32 i = 0; i < ARRAY_SIZE(conns); i++) {
		conns[i].fd = createTcpServer(0, conns[i].port);
		if(conns[i].fd < 0) {
			ERROR("create server failed, name = %s\n", conns[i].name.c_str());
			return ;
		}
		
#ifdef U_LOOP
		memset(&conns[i].h, 0, sizeof(conns[i].h));
		conns[i].h.data = &conns[i];
		conns[i].h.fd = conns[i].fd;
		conns[i].h.cb = acceptConnection;
		uloop_fd_add(&conns[i].h, ULOOP_READ);
#else		
		conns[i].h.data = &conns[i];
		ev_io_init (&conns[i].h, acceptConnection, conns[i].fd, EV_READ);
		ev_io_start (loop, &conns[i].h);
#endif	

		INFO("create %s server success, port = %d\n", 
					conns[i].name.c_str(), conns[i].port);
	}
	
	return;
}

	
